from decimal import Decimal
from django.utils.timezone import now
from django_redis import get_redis_connection
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from goods.models import SKU
from meiduo_mall.utils.exceptions import logger
from orders.models import OrderInfo, OrderGoods

from django.db import transaction
from celery_tasks.html.task import generate_static_sku_detail_html


class CartSKUSerializer(serializers.ModelSerializer):
    """购物车商品数据序列化器"""

    count = serializers.IntegerField(label='数量')

    class Meta:
        model = SKU
        fields = ('id', 'name', 'default_image_url', 'price', 'count')


class OrderSettlementSerializer(serializers.Serializer):
    """订单数据序列化器"""

    freight = serializers.DecimalField(max_digits=10, decimal_places=2, label='运费')
    skus = CartSKUSerializer(many=True)


class SaveOrderSerializer(serializers.ModelSerializer):
    """保存订单信息序列化器"""

    class Meta:
        model = OrderInfo
        fields = ('order_id', 'address', 'pay_method')
        read_only_fields = ('order_id',)
        extra_kwargs = {
            'address': {'write_only': True, 'required': True},
            'pay_method': {'write_only': True, 'required': True},
        }

    def create(self, validated_data):
        """保存订单"""

        # 获取当前提交订单的用户
        user = self.context.get('request').user

        # 生成订单编号 20180704174607000000001
        order_id = now().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
        address = validated_data.get('address')
        pay_method = validated_data.get('pay_method')

        with transaction.atomic():
            """
            # 创建保存点
            save_id = transaction.savepoint()

            # 回滚到保存点
            transaction.savepoint_rollback(save_id)

            # 提交从保存点到当前状态的所有数据库事务操作
            transaction.savepoint_commit(save_id)
            """
            # 创建事务保存点
            save_id = transaction.savepoint()
            # 保存信息到OrderInfo
            try:
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal('0'),
                    freight=Decimal('10.00'),
                    pay_method=pay_method,
                    status=1 if pay_method == 2 else 2
                )
            # 从redis中获取购物车结算商品数据
                redis_conn = get_redis_connection('cart')
                sku_dict = redis_conn.hgetall('cart_%s' % user.id)
                selected = redis_conn.smembers('cart_selected_%s' % user.id)
            # 遍历结算商品信息
                cart = {}
                for sku_id in selected:
                    cart[int(sku_id)] = int(sku_dict[sku_id])
            #   判断是否有库存
                sku_ids = cart.keys()
                for sku_id in sku_ids:
                    while True:
                        sku = SKU.objects.get(id=sku_id)
                        # 获取库存和销量
                        origin_stock = sku.stock
                        origin_sales = sku.sales
                        count = cart[sku_id]
                        # 判断是否库存够
                        if count > origin_stock:
                            # 库存不足，回滚
                            transaction.savepoint_rollback(save_id)
                            raise serializers.ValidationError('%s库存不足，库存只剩%s' % (sku.name, sku.stock))
                #   减小库存，增加销量
                        # 修改sku表的库存和销量
                        new_stock = origin_stock - count
                        new_sales = origin_sales + count
                        # 乐观锁更新库存
                        result = SKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock, sales=new_sales)
                        # 如果下单失败但库存足够，则继续下单，直到下单成功或者库存确实不足停止
                        if result == 0:
                            continue
                        # 修改spu表的销量
                        sku.goods.sales += count
                        sku.goods.save()
                #   保存订单信息
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=count,
                            price=sku.price
                        )
                        # 累加订单总金额和数量
                        order.total_count += count
                        order.total_amount += (count * sku.price)

                        # 支付成功
                        break
                # 支付总额
                order.total_amount += order.freight
                order.save()
            except ValidationError:
                raise  # 库存不足已回滚
            except Exception as e:
                transaction.savepoint_rollback(save_id)
                logger.error('下单失败: %s' % e)
                raise

            # 提交事务
            transaction.savepoint_commit(save_id)
            for sku_id in sku_ids:
                # 更新商品详情页
                generate_static_sku_detail_html.delay(sku_id)
            # 在redis购物车中删除已计算商品数据
            pl = redis_conn.pipeline()
            pl.hdel('cart_%s' % user.id, *selected)
            pl.srem('cart_selected_%s' % user.id, *selected)
            pl.execute()

            # 保存完成
            return order


class SKUSerializer(serializers.ModelSerializer):

    class Meta:
        model = SKU
        fields = ('id', 'name', 'default_image_url')


class OrderGoodsSerializer(serializers.ModelSerializer):
    """订单中商品信息"""
    sku = SKUSerializer()

    class Meta:
        model = OrderGoods
        fields = ('price', 'count', 'sku')


class OrderSerializer(serializers.ModelSerializer):
    """展示订单序列化器"""

    skus = OrderGoodsSerializer(many=True)

    # create_time: "2018-11-16T10:15:07.805361"

    class Meta:
        model = OrderInfo
        fields = ('create_time', 'order_id', 'total_amount', 'freight', 'pay_method', 'status', 'skus')
